﻿using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using Microsoft.VisualStudio.TextTemplating;
using T4UtilityBelt.Applications;
using T4UtilityBelt.DataDesigns;
using T4UtilityBelt.DataDesigns.Renderers;
using T4UtilityBelt.Serializers;
using T4UtilityBelt.Utilities;
using T4UtilityBelt.UxDesigns;

namespace T4UtilityBelt.Templates
{
    public abstract class Template : TextTransformation
    {
        protected Template()
        {
            _entityPropertyRenderers = new RendererCollection(this);
            Platform = Platforms.WindowsTablet;
        }

        #region Properties

        private readonly RendererCollection _entityPropertyRenderers;
        private DataDesigns.EntityModel _model;
        private TemplateFileManager _templateFileManager;

        public RendererCollection EntityPropertyRenderers
        {
            get { return _entityPropertyRenderers; }
        }

        /// <summary>
        ///     The namespace of the UI classes
        /// </summary>
        public string Namespace
        {
            get { return Application.Namespace; }
            //set { Application.Namespace = value; }
        }

        public Application Application
        {
            get { return _application; }
        }

        public Platforms Platform { get; set; }

        public EntityCollection Entities
        {
            get { return Model.Entities; }
        }

        /// <summary>
        /// </summary>
        protected DataDesigns.EntityModel Model
        {
            get { return _model; }
        }

        protected TemplateFileManager TemplateFileManager
        {
            get
            {
                if (_templateFileManager == null)
                {
                    _templateFileManager = TemplateFileManager.Create(this);
                }
                return _templateFileManager;
            }
        }

        public override void Initialize()
        {
            base.Initialize();

            InitializeApplication();

            InitializeEntityModel();
            if (_model != null)
            {
                _model.FixModel();
            }
        }

        protected virtual void InitializeApplication()
        {
            _application = new Application();
        }

        protected virtual void InitializeEntityModel()
        {
            _model = LoadModel();
        }

        public string TemplatePath
        {
            get 
            { 
                return Path.GetDirectoryName(this.TemplateFileManager.TemplateProjectItem.ContainingProject.DTE.Solution.FileName);
            }
        }

        protected void LoadApplication(string fileName)
        {
            XSerializer serializer = new XSerializer();

            _application = serializer.Deserialize<Application>(XDocument.Load(Path.Combine(TemplatePath, fileName)));
        }

        protected void LoadEntityModel(string fileName)
        {
            XSerializer serializer = new XSerializer();

            DataDesigns.EntityModel model = serializer.Deserialize<DataDesigns.EntityModel>(XDocument.Load(Path.Combine(TemplatePath, fileName)));

            if (_model == null)
            {
                _model = model;
            }
            else
            {
                if (model.Entities != null)
                {
                    foreach (Entity entity in model.Entities)
                    {
                        _model.Entities.Add(entity);
                    }
                }
            }
        }

        protected void LoadEntities(string folder)
        {
            string[] files = Directory.GetFiles(Path.Combine(TemplatePath, folder), "*.tlex");

            XSerializer serializer = new XSerializer();

            foreach (string file in files)
            {
                DataDesigns.Entity entity = serializer.Deserialize<DataDesigns.Entity>(XDocument.Load(file));

                if (_model == null)
                {
                    _model = new DataDesigns.EntityModel();
                }

                _model.Entities.Add(entity);
            }
        }

        protected void LoadEntity(string fileName)
        {
            XSerializer serializer = new XSerializer();

            DataDesigns.Entity entity = serializer.Deserialize<DataDesigns.Entity>(XDocument.Load(Path.Combine(TemplatePath, fileName)));

            if (_model == null)
            {
                _model = new DataDesigns.EntityModel();
            }

            _model.Entities.Add(entity);
        }

        protected virtual DataDesigns.EntityModel LoadModel()
        {
            return new DataDesigns.EntityModel();
        }

        #endregion

        #region Methods Template File Manager

        protected bool StartNewFile(string name)
        {
            return TemplateFileManager.StartNewFile(name);
        }

        protected void ProcessFiles()
        {
            TemplateFileManager.Process();
        }

        #endregion

        #region ToString Helpers

        private readonly ToStringInstanceHelper _toStringHelperField = new ToStringInstanceHelper();
        private Application _application;

        public ToStringInstanceHelper ToStringHelper
        {
            get { return _toStringHelperField; }
        }

        public void RenderEntityProperties(Entity entity)
        {
            RenderEntityProperties(entity.Properties);
        }

        public void RenderEntityProperties(PropertyCollection properties)
        {
            foreach (Property property in properties)
            {
                IRenderer renderer = EntityPropertyRenderers.FindRenderer(property);
                if (renderer != null)
                {
                    renderer.RenderProperty(property);
                }
            }
        }

        public void Render2EntityProperties(Entity entity)
        {
            Render2EntityProperties(entity.Properties);
        }

        public void Render2EntityProperties(PropertyCollection properties)
        {
            foreach (Property property in properties)
            {
                IRenderer renderer = EntityPropertyRenderers.FindRenderer(property);
                if (renderer != null)
                {
                    renderer.Render2Property(property);
                }
            }
        }

        public void Render3EntityProperties(Entity entity)
        {
            Render3EntityProperties(entity.Properties);
        }

        public void Render3EntityProperties(PropertyCollection properties)
        {
            foreach (Property property in properties)
            {
                IRenderer renderer = EntityPropertyRenderers.FindRenderer(property);
                if (renderer != null)
                {
                    renderer.Render3Property(property);
                }
            }
        }

        public string ExceptionToString(Exception exception)
        {
            if (exception == null)
                return "";
            return string.Format(@"
Type: {0}
Message: {1}
StackTrace {2}
Inner: {3}
", exception.GetType().FullName, exception.Message, exception.StackTrace, ExceptionToString(exception.InnerException));
        }

        /// <summary>
        ///     Utility class to produce culture-oriented representation of an object as a string.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
        public class ToStringInstanceHelper
        {
            private IFormatProvider _formatProviderField = CultureInfo.InvariantCulture;

            /// <summary>
            ///     Gets or sets format provider to be used by ToStringWithCulture method.
            /// </summary>
            public IFormatProvider FormatProvider
            {
                get { return _formatProviderField; }
                set
                {
                    if ((value != null))
                    {
                        _formatProviderField = value;
                    }
                }
            }

            /// <summary>
            ///     This is called from the compile/run appdomain to convert objects within an expression block to a string
            /// </summary>
            public string ToStringWithCulture(object value)
            {
                if ((value == null))
                {
                    throw new ArgumentNullException("value");
                }
                Type t = value.GetType();
                MethodInfo method = t.GetMethod("ToString", new[]
                    {
                        typeof (IFormatProvider)
                    });
                if ((method == null))
                {
                    return value.ToString();
                }
                return ((string)(method.Invoke(value, new object[]
                    {
                        _formatProviderField
                    })));
            }
        }

        #endregion

        #region String Helpers

        protected void WriteAutoGeneratedComment(string version)
        {
            Write(@"//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from the {0} T4 template v{1}.
//     Generation date: {2}
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
", GetType().Name, version, DateTime.Now);
        }

        #endregion
    }
}